package com.ruoyi.common.utils.opcua;

import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfig;
import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
import org.eclipse.milo.opcua.sdk.client.api.identity.IdentityProvider;
import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider;
import org.eclipse.milo.opcua.stack.client.DiscoveryClient;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.UaException;
import org.eclipse.milo.opcua.stack.core.types.builtin.*;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;
import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
import org.eclipse.milo.opcua.stack.core.types.structured.WriteValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.net.URISyntaxException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 增强版 OPC UA 工具类
 * 提供静态方法和批量操作支持，优化连接管理和认证方式
 * 支持写入时自动类型转换，兼容低版本Milo库
 */
public class OpcUaUtil {
    private static final Logger logger = LoggerFactory.getLogger(OpcUaUtil.class);

    // 默认连接超时时间（秒）
    private static final long DEFAULT_CONNECTION_TIMEOUT = 30;
    // 默认操作超时时间（秒）
    private static final long DEFAULT_OPERATION_TIMEOUT = 10;
    // 批量操作的默认批次大小
    private static final int DEFAULT_BATCH_SIZE = 50;
    // 客户端连接池
    private static final ConcurrentHashMap<String, ClientPoolEntry> CLIENT_POOL = new ConcurrentHashMap<>();
    // 连接池最大数量
    private static final int MAX_POOL_SIZE = 10;
    // 连接空闲超时时间（秒）
    private static final long IDLE_TIMEOUT = 300;

    // 安全模式字符串常量（替代SecurityMode枚举）
    private static final String SECURITY_MODE_NONE = "None";
    private static final String SECURITY_MODE_SIGN = "Sign";
    private static final String SECURITY_MODE_SIGN_ENCRYPT = "SignAndEncrypt";
    // 安全策略字符串常量
    private static final String SECURITY_POLICY_NONE = "http://opcfoundation.org/UA/SecurityPolicy#None";

    public static void main(String[] args) {
        try {
            logger.info("开始执行OPC UA客户端测试");
            String endpointUrl = "opc.tcp://192.168.0.195:12688";

            // 测试写入操作
            Map<String, Object> nodeValueMap = new HashMap<>();
            nodeValueMap.put("ns=2;s=1M-004", "135.5");  // 字符串将被转换为目标类型
            nodeValueMap.put("ns=2;s=1M-005", 121.3);   // 数值将被转换为目标类型
            nodeValueMap.put("ns=2;s=1M-006", 53.7f);

            logger.info("准备批量写入 {} 个节点值到服务器: {}", nodeValueMap.size(), endpointUrl);
            Map<String, StatusCode> results = OpcUaUtil.writeValues(endpointUrl, nodeValueMap);

            // 检查写入结果
            for (Map.Entry<String, StatusCode> entry : results.entrySet()) {
                if (entry.getValue().isGood()) {
                    logger.info("节点 [{}] 写入成功", entry.getKey());
                } else {
                    logger.error("节点 [{}] 写入失败，状态码: {}", entry.getKey(), entry.getValue());
                }
            }

            // 测试读取操作
            List<String> nodeIds = new ArrayList<>();
            nodeIds.add("ns=2;s=1M-004");
            nodeIds.add("ns=2;s=1M-005");
            nodeIds.add("ns=2;s=1M-006");

            logger.info("准备批量读取 {} 个节点值从服务器: {}", nodeIds.size(), endpointUrl);
            Map<String, String> stringMap = OpcUaUtil.readValuesAsString(endpointUrl, nodeIds);

            for (Map.Entry<String, String> entry : stringMap.entrySet()) {
                if (entry.getValue() != null) {
                    logger.info("节点 [{}] 读取成功，值: {}", entry.getKey(), entry.getValue());
                } else {
                    logger.warn("节点 [{}] 读取值为空或节点不存在", entry.getKey());
                }
            }

            logger.info("OPC UA客户端测试执行完成");
        } catch (OpcUaUtil.OpcUaException e) {
            logger.error("OPC UA操作发生异常", e);
        }
    }

    /**
     * 私有构造函数，防止实例化
     */
    private OpcUaUtil() {
        throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
    }

    /**
     * 读取单个节点值（匿名认证）
     */
    public static DataValue readValue(String endpointUrl, String nodeId) throws OpcUaException {
        logger.info("读取单个节点值 - 节点: {}, 服务器: {}", nodeId, endpointUrl);
        return readValue(endpointUrl, nodeId, null, null);
    }

    /**
     * 读取单个节点值（支持用户名密码认证）
     */
    public static DataValue readValue(String endpointUrl, String nodeId, String username, String password) throws OpcUaException {
        logger.info("读取单个节点值 - 节点: {}, 服务器: {}, 用户: {}", nodeId, endpointUrl, username != null ? username : "anonymous");

        return executeWithConnection(endpointUrl, username, password, client -> {
            try {
                return readSingleValue(client, nodeId).get(DEFAULT_OPERATION_TIMEOUT, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new OpcUaException("读取操作被中断: " + nodeId, e);
            } catch (ExecutionException e) {
                throw new OpcUaException("读取操作执行失败: " + nodeId, e.getCause());
            } catch (TimeoutException e) {
                throw new OpcUaException("读取操作超时 (" + DEFAULT_OPERATION_TIMEOUT + "秒): " + nodeId, e);
            }
        });
    }

    /**
     * 批量读取节点值（匿名认证，使用默认批次大小）
     */
    public static Map<String, DataValue> readValues(String endpointUrl, List<String> nodeIds) throws OpcUaException {
        logger.info("批量读取节点值 - 节点数量: {}, 服务器: {}", nodeIds.size(), endpointUrl);
        return readValues(endpointUrl, nodeIds, null, null, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量读取节点值（支持用户名密码认证，使用默认批次大小）
     */
    public static Map<String, DataValue> readValues(String endpointUrl, List<String> nodeIds, String username, String password) throws OpcUaException {
        return readValues(endpointUrl, nodeIds, username, password, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量读取节点值（支持分批处理和认证）
     */
    public static Map<String, DataValue> readValues(String endpointUrl, List<String> nodeIds, String username, String password, int batchSize) throws OpcUaException {
        logger.info("批量读取节点值 - 节点数量: {}, 批次大小: {}, 服务器: {}, 用户: {}", nodeIds.size(), batchSize, endpointUrl, username != null ? username : "anonymous");

        // 分批处理
        List<List<String>> batches = splitIntoBatches(nodeIds, batchSize);
        logger.debug("将 {} 个节点分成 {} 批处理", nodeIds.size(), batches.size());

        Map<String, DataValue> result = new HashMap<>();

        for (int i = 0; i < batches.size(); i++) {
            List<String> batch = batches.get(i);
            logger.debug("处理读取批次 {} / {}，包含 {} 个节点", i + 1, batches.size(), batch.size());

            Map<String, DataValue> batchResult = executeWithConnection(endpointUrl, username, password, client -> {
                try {
                    return readMultipleValues(client, batch).get(DEFAULT_OPERATION_TIMEOUT, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new OpcUaException("批次读取被中断", e);
                } catch (ExecutionException e) {
                    throw new OpcUaException("批次读取执行失败", e.getCause());
                } catch (TimeoutException e) {
                    throw new OpcUaException("批次读取超时 (" + DEFAULT_OPERATION_TIMEOUT + "秒)", e);
                }
            });

            result.putAll(batchResult);
            logger.debug("完成读取批次 {} / {}，成功获取 {} 个节点值", i + 1, batches.size(), batchResult.size());
        }

        logger.info("批量读取完成，共获取 {} 个节点值", result.size());
        return result;
    }

    /**
     * 写入单个节点值（匿名认证）
     */
    public static StatusCode writeValue(String endpointUrl, String nodeId, Object value) throws OpcUaException {
        logger.info("写入单个节点值 - 节点: {}, 值: {}, 服务器: {}", nodeId, value, endpointUrl);
        return writeValue(endpointUrl, nodeId, value, null, null);
    }

    /**
     * 写入单个节点值（支持用户名密码认证）
     */
    public static StatusCode writeValue(String endpointUrl, String nodeId, Object value, String username, String password) throws OpcUaException {
        logger.info("写入单个节点值 - 节点: {}, 值: {}, 服务器: {}, 用户: {}", nodeId, value, endpointUrl, username != null ? username : "anonymous");

        return executeWithConnection(endpointUrl, username, password, client -> {
            try {
                // 先读取当前值获取类型，再转换后写入
                return writeSingleValueWithConversion(client, nodeId, value).get(DEFAULT_OPERATION_TIMEOUT, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new OpcUaException("写入操作被中断: " + nodeId, e);
            } catch (ExecutionException e) {
                throw new OpcUaException("写入操作执行失败: " + nodeId, e.getCause());
            } catch (TimeoutException e) {
                throw new OpcUaException("写入操作超时 (" + DEFAULT_OPERATION_TIMEOUT + "秒): " + nodeId, e);
            }
        });
    }

    /**
     * 批量写入节点值（匿名认证，使用默认批次大小）
     */
    public static Map<String, StatusCode> writeValues(String endpointUrl, Map<String, Object> nodeValueMap) throws OpcUaException {
        logger.info("批量写入节点值 - 节点数量: {}, 服务器: {}", nodeValueMap.size(), endpointUrl);
        return writeValues(endpointUrl, nodeValueMap, null, null, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量写入节点值（支持用户名密码认证，使用默认批次大小）
     */
    public static Map<String, StatusCode> writeValues(String endpointUrl, Map<String, Object> nodeValueMap, String username, String password) throws OpcUaException {
        return writeValues(endpointUrl, nodeValueMap, username, password, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量写入节点值（支持分批处理和认证）
     */
    public static Map<String, StatusCode> writeValues(String endpointUrl, Map<String, Object> nodeValueMap, String username, String password, int batchSize) throws OpcUaException {
        logger.info("批量写入节点值 - 节点数量: {}, 批次大小: {}, 服务器: {}, 用户: {}", nodeValueMap.size(), batchSize, endpointUrl, username != null ? username : "anonymous");

        // 分批处理
        List<Map<String, Object>> batches = splitMapIntoBatches(nodeValueMap, batchSize);
        logger.debug("将 {} 个节点分成 {} 批处理", nodeValueMap.size(), batches.size());

        Map<String, StatusCode> result = new HashMap<>();

        for (int i = 0; i < batches.size(); i++) {
            Map<String, Object> batch = batches.get(i);
            logger.debug("处理写入批次 {} / {}，包含 {} 个节点", i + 1, batches.size(), batch.size());

            Map<String, StatusCode> batchResult = executeWithConnection(endpointUrl, username, password, client -> {
                try {
                    // 批量写入时对每个值进行类型转换
                    return writeMultipleValuesWithConversion(client, batch).get(DEFAULT_OPERATION_TIMEOUT, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new OpcUaException("批次写入被中断", e);
                } catch (ExecutionException e) {
                    throw new OpcUaException("批次写入执行失败", e.getCause());
                } catch (TimeoutException e) {
                    throw new OpcUaException("批次写入超时 (" + DEFAULT_OPERATION_TIMEOUT + "秒)", e);
                }
            });

            result.putAll(batchResult);
            logger.debug("完成写入批次 {} / {}，处理 {} 个节点", i + 1, batches.size(), batchResult.size());
        }

        logger.info("批量写入完成，共处理 {} 个节点", result.size());
        return result;
    }

    /**
     * 执行带连接的操作（使用连接池）
     */
    private static <T> T executeWithConnection(String endpointUrl, String username, String password, Function<OpcUaClient, T> operation) throws OpcUaException {
        String poolKey = createPoolKey(endpointUrl, username);
        logger.debug("执行带连接的操作，连接池键: {}", poolKey);

        OpcUaClient client = getClientFromPool(poolKey, endpointUrl, username, password);

        try {
            long startTime = System.currentTimeMillis();
            T result = operation.apply(client);
            long duration = System.currentTimeMillis() - startTime;
            logger.debug("操作执行完成，耗时: {}ms", duration);
            return result;
        } catch (OpcUaException e) {
            logger.error("操作执行失败", e);
            throw e;
        } catch (Exception e) {
            // 操作失败时从池中移除客户端，下次将创建新连接
            CLIENT_POOL.remove(poolKey);
            logger.error("操作执行发生意外异常，已从连接池移除客户端，键: {}", poolKey, e);
            throw new OpcUaException("Operation failed", e);
        }
    }

    /**
     * 从连接池获取客户端，不存在则创建
     */
    private static OpcUaClient getClientFromPool(String poolKey, String endpointUrl, String username, String password) throws OpcUaException {
        // 检查连接池是否有可用客户端
        ClientPoolEntry entry = CLIENT_POOL.get(poolKey);
        if (entry != null && !entry.isExpired()) {
            entry.updateLastUsedTime();
            logger.info("从连接池复用客户端，键: {}, 连接池当前大小: {}", poolKey, CLIENT_POOL.size());
            return entry.client;
        }

        // 连接池已满，移除过期连接
        if (CLIENT_POOL.size() >= MAX_POOL_SIZE) {
            logger.warn("连接池已达最大容量: {}, 清理过期连接", MAX_POOL_SIZE);
            cleanupExpiredClients();

            // 清理后仍满则抛出异常
            if (CLIENT_POOL.size() >= MAX_POOL_SIZE) {
                logger.error("连接池清理后仍满，最大容量: {}", MAX_POOL_SIZE);
                throw new OpcUaException("Client pool is full, max size: " + MAX_POOL_SIZE);
            }
        }

        // 创建新客户端并加入连接池
        OpcUaClient client = createAndConnectClient(endpointUrl, username, password);
        CLIENT_POOL.put(poolKey, new ClientPoolEntry(client));
        logger.info("创建新客户端并加入连接池，键: {}, 连接池当前大小: {}", poolKey, CLIENT_POOL.size());

        return client;
    }

    /**
     * 清理过期的客户端连接
     */
    private static void cleanupExpiredClients() {
        int initialSize = CLIENT_POOL.size();

        CLIENT_POOL.entrySet().removeIf(entry -> {
            ClientPoolEntry poolEntry = entry.getValue();
            boolean expired = poolEntry.isExpired();

            if (expired) {
                try {
                    logger.debug("清理过期客户端连接，键: {}", entry.getKey());
                    poolEntry.client.disconnect().get(5, TimeUnit.SECONDS);
                    logger.info("成功断开过期客户端连接，键: {}", entry.getKey());
                } catch (Exception e) {
                    logger.warn("断开过期客户端连接失败，键: {}", entry.getKey(), e);
                }
            }
            return expired;
        });

        logger.info("连接池清理完成，清理前大小: {}, 清理后大小: {}, 移除过期连接数: {}", initialSize, CLIENT_POOL.size(), initialSize - CLIENT_POOL.size());
    }

    /**
     * 创建连接池键
     */
    private static String createPoolKey(String endpointUrl, String username) {
        return endpointUrl + "|" + (username == null ? "anonymous" : username);
    }

    /**
     * 创建并连接客户端（支持匿名和用户名密码认证）
     */
    private static OpcUaClient createAndConnectClient(String endpointUrl, String username, String password) throws OpcUaException {
        try {
            logger.info("开始创建并连接到OPC UA服务器: {}, 用户: {}", endpointUrl, username != null ? username : "anonymous");

            // 获取端点列表
            logger.debug("正在发现服务器端点: {}", endpointUrl);
            long discoveryStartTime = System.currentTimeMillis();
            List<EndpointDescription> endpoints = DiscoveryClient.getEndpoints(endpointUrl).get(DEFAULT_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
            long discoveryDuration = System.currentTimeMillis() - discoveryStartTime;

            logger.debug("服务器端点发现完成，耗时: {}ms，发现 {} 个端点", discoveryDuration, endpoints != null ? endpoints.size() : 0);

            if (endpoints == null || endpoints.isEmpty()) {
                logger.error("未找到服务器端点: {}", endpointUrl);
                throw new OpcUaException("No endpoints found for: " + endpointUrl);
            }

            // 选择合适的端点
            EndpointDescription endpoint = selectBestEndpoint(endpoints);
            // 关键修复：将端点URL中的localhost/127.0.0.1替换为实际服务器IP
            String serverIp = endpointUrl.split("//")[1].split(":")[0];
            String originalEndpointUrl = endpoint.getEndpointUrl();
            String correctedEndpointUrl = replaceLocalhostWithIp(originalEndpointUrl, serverIp);
            if (!originalEndpointUrl.equals(correctedEndpointUrl)) {
                logger.warn("端点地址修正: {} -> {}", originalEndpointUrl, correctedEndpointUrl);
                // 创建新的端点描述符，使用修正后的URL
                endpoint = new EndpointDescription(
                        correctedEndpointUrl,
                        endpoint.getServer(),
                        endpoint.getServerCertificate(),
                        endpoint.getSecurityMode(),
                        endpoint.getSecurityPolicyUri(),
                        endpoint.getUserIdentityTokens(),
                        endpoint.getTransportProfileUri(),
                        endpoint.getSecurityLevel()
                );
            }

            logger.info("选择的服务器端点 - 地址: {}, 安全策略: {}, 安全模式: {}", endpoint.getEndpointUrl(), endpoint.getSecurityPolicyUri(), getSecurityModeName(endpoint));

            // 创建身份认证器
            IdentityProvider identityProvider = createIdentityProvider(username, password);

            // 创建客户端配置
            OpcUaClientConfig config = OpcUaClientConfig.builder()
                    .setEndpoint(endpoint)
                    .setIdentityProvider(identityProvider)
                    .setRequestTimeout(UInteger.valueOf(DEFAULT_OPERATION_TIMEOUT * 1000))
                    .build();

            // 创建客户端实例并连接
            logger.debug("正在创建客户端并连接到服务器...");
            long connectStartTime = System.currentTimeMillis();
            OpcUaClient client = OpcUaClient.create(config);
            client.connect().get(DEFAULT_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
            long connectDuration = System.currentTimeMillis() - connectStartTime;

            logger.info("成功连接到OPC UA服务器，耗时: {}ms，服务器地址: {}", connectDuration, endpointUrl);
            return client;

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            logger.error("连接服务器被中断", e);
            throw new OpcUaException("Connection interrupted to OPC UA server: " + endpointUrl, e);
        } catch (ExecutionException e) {
            logger.error("连接服务器执行失败", e.getCause());
            throw new OpcUaException("Connection failed to OPC UA server: " + endpointUrl, e.getCause());
        } catch (TimeoutException e) {
            logger.error("连接服务器超时 ({}秒)", DEFAULT_CONNECTION_TIMEOUT, e);
            throw new OpcUaException("Connection timeout to OPC UA server: " + endpointUrl, e);
        } catch (UaException e) {
            logger.error("OPC UA协议异常", e);
            throw new OpcUaException("OPC UA protocol error: " + endpointUrl, e);
        }
    }

    /**
     * 将URL中的localhost或127.0.0.1替换为实际IP
     */
    private static String replaceLocalhostWithIp(String url, String actualIp) {
        try {
            URI uri = new URI(url);
            String host = uri.getHost();

            // 如果主机是localhost或127.0.0.1，则替换为实际IP
            if (host != null && (host.equals("localhost") || host.equals("127.0.0.1"))) {
                // 构建新的URL
                return String.format("%s://%s:%d%s",
                        uri.getScheme(),
                        actualIp,
                        uri.getPort(),
                        uri.getPath() != null ? uri.getPath() : ""
                );
            }
        } catch (URISyntaxException e) {
            logger.warn("解析URL失败: {}", url, e);
        }
        return url;
    }

    /**
     * 选择最佳端点（通过字符串匹配安全模式，不直接依赖SecurityMode类）
     */
    private static EndpointDescription selectBestEndpoint(List<EndpointDescription> endpoints) {
        // 优先选择无安全策略且无安全模式的端点
        return endpoints.stream()
                .filter(e -> SECURITY_POLICY_NONE.equals(e.getSecurityPolicyUri()) &&
                        SECURITY_MODE_NONE.equals(getSecurityModeName(e)))
                .findFirst()
                // 其次选择签名加密的端点
                .orElseGet(() -> endpoints.stream()
                        .filter(e -> SECURITY_MODE_SIGN_ENCRYPT.equals(getSecurityModeName(e)))
                        .findFirst()
                        // 再选择仅签名的端点
                        .orElseGet(() -> endpoints.stream()
                                .filter(e -> SECURITY_MODE_SIGN.equals(getSecurityModeName(e)))
                                .findFirst()
                                // 最后选择第一个端点
                                .orElse(endpoints.get(0))));
    }

    /**
     * 获取安全模式名称（兼容不同版本Milo库）
     */
    private static String getSecurityModeName(EndpointDescription endpoint) {
        // 通过toString()或name()方法获取安全模式名称，避免直接引用枚举类
        Object securityMode = endpoint.getSecurityMode();
        if (securityMode != null) {
            // 优先尝试name()方法（枚举类方法）
            try {
                return securityMode.getClass().getMethod("name").invoke(securityMode).toString();
            } catch (Exception e) {
                // 失败时使用toString()方法
                return securityMode.toString();
            }
        }
        return "";
    }

    /**
     * 创建身份认证器
     */
    private static IdentityProvider createIdentityProvider(String username, String password) {
        if (username != null && password != null) {
            logger.debug("使用用户名密码认证，用户: {}", username);
            return new UsernameProvider(username, password);
        } else {
            logger.debug("使用匿名认证");
            return new AnonymousProvider();
        }
    }

    /**
     * 读取单个节点值
     */
    private static CompletableFuture<DataValue> readSingleValue(OpcUaClient client, String nodeId) {
        NodeId targetNodeId = NodeId.parse(nodeId);
        ReadValueId readValueId = new ReadValueId(
                targetNodeId,
                AttributeId.Value.uid(),
                null,
                QualifiedName.NULL_VALUE
        );

        return client.read(0, TimestampsToReturn.Both, Collections.singletonList(readValueId))
                .thenApply(readResponse -> {
                    DataValue[] dataValues = readResponse.getResults();
                    if (dataValues != null && dataValues.length > 0) {
                        StatusCode statusCode = dataValues[0].getStatusCode();
                        if (statusCode != null && statusCode.isGood()) {
                            logger.debug("节点 [{}] 读取成功，状态码: {}", nodeId, statusCode);
                        } else {
                            logger.warn("节点 [{}] 读取返回非成功状态码: {}", nodeId, statusCode);
                        }
                        return dataValues[0];
                    } else {
                        logger.error("节点 [{}] 读取失败，未返回结果", nodeId);
                        throw new OpcUaException("Read failed for node: " + nodeId);
                    }
                })
                .exceptionally(ex -> {
                    logger.error("节点 [{}] 读取发生异常", nodeId, ex);
                    throw new OpcUaException("Read failed for node: " + nodeId, ex);
                });
    }

    /**
     * 批量读取节点值
     */
    private static CompletableFuture<Map<String, DataValue>> readMultipleValues(OpcUaClient client, List<String> nodeIds) {
        List<ReadValueId> readValueIds = nodeIds.stream()
                .map(nodeId -> new ReadValueId(
                        NodeId.parse(nodeId),
                        AttributeId.Value.uid(),
                        null,
                        QualifiedName.NULL_VALUE
                ))
                .collect(Collectors.toList());

        return client.read(0, TimestampsToReturn.Both, readValueIds)
                .thenApply(readResponse -> {
                    DataValue[] dataValues = readResponse.getResults();
                    if (dataValues == null || dataValues.length != nodeIds.size()) {
                        logger.error("批量读取结果数量不匹配，预期: {}, 实际: {}",
                                nodeIds.size(), dataValues == null ? 0 : dataValues.length);
                        throw new OpcUaException("Read failed: result count mismatch, expected " +
                                nodeIds.size() + ", got " + (dataValues == null ? 0 : dataValues.length));
                    }

                    Map<String, DataValue> result = new HashMap<>();
                    int successCount = 0;

                    for (int i = 0; i < nodeIds.size(); i++) {
                        String nodeId = nodeIds.get(i);
                        DataValue value = dataValues[i];
                        result.put(nodeId, value);

                        if (value.getStatusCode() != null && value.getStatusCode().isGood()) {
                            successCount++;
                        } else {
                            logger.warn("节点 [{}] 读取返回非成功状态码: {}", nodeId, value.getStatusCode());
                        }
                    }

                    logger.debug("批量读取完成，总节点数: {}, 成功读取: {}", nodeIds.size(), successCount);
                    return result;
                })
                .exceptionally(ex -> {
                    logger.error("批量读取发生异常", ex);
                    throw new OpcUaException("Batch read failed", ex);
                });
    }

    /**
     * 带类型转换的单个节点写入
     */
    private static CompletableFuture<StatusCode> writeSingleValueWithConversion(OpcUaClient client, String nodeId, Object value) {
        // 先读取节点当前值获取类型信息
        return readSingleValue(client, nodeId)
                .thenCompose(currentDataValue -> {
                    try {
                        // 获取当前值的类型并转换新值
                        Object currentValue = currentDataValue.getValue() != null ? currentDataValue.getValue().getValue() : null;
                        Object convertedValue = convertValueToMatchType(currentValue, value);

                        logger.debug("节点 [{}] 类型转换 - 原始值: {}({}), 转换后: {}({})",
                                nodeId,
                                value, value != null ? value.getClass().getSimpleName() : "null",
                                convertedValue, convertedValue != null ? convertedValue.getClass().getSimpleName() : "null");

                        // 执行实际写入
                        NodeId targetNodeId = NodeId.parse(nodeId);
                        Variant variant = new Variant(convertedValue);
                        DataValue dataValue = new DataValue(variant, null, null);

                        WriteValue writeValue = new WriteValue(
                                targetNodeId,
                                AttributeId.Value.uid(),
                                null,
                                dataValue
                        );

                        return client.write(Collections.singletonList(writeValue))
                                .thenApply(writeResponse -> {
                                    StatusCode[] statusCodes = writeResponse.getResults();
                                    if (statusCodes != null && statusCodes.length > 0) {
                                        StatusCode statusCode = statusCodes[0];
                                        if (statusCode.isGood()) {
                                            logger.debug("节点 [{}] 写入成功，转换后值: {}", nodeId, convertedValue);
                                        } else {
                                            logger.error("节点 [{}] 写入失败，转换后值: {}, 状态码: {}", nodeId, convertedValue, statusCode);
                                        }
                                        return statusCode;
                                    } else {
                                        logger.error("节点 [{}] 写入失败，未返回结果，转换后值: {}", nodeId, convertedValue);
                                        throw new OpcUaException("Write failed for node: " + nodeId);
                                    }
                                });
                    } catch (Exception e) {
                        logger.error("节点 [{}] 类型转换失败", nodeId, e);
                        throw new OpcUaException("Type conversion failed for node: " + nodeId, e);
                    }
                })
                .exceptionally(ex -> {
                    logger.error("节点 [{}] 写入发生异常", nodeId, ex);
                    throw new OpcUaException("Write failed for node: " + nodeId, ex);
                });
    }

    /**
     * 带类型转换的批量节点写入
     */
    private static CompletableFuture<Map<String, StatusCode>> writeMultipleValuesWithConversion(OpcUaClient client, Map<String, Object> nodeValueMap) {
        // 先批量读取所有节点的当前值
        List<String> nodeIds = new ArrayList<>(nodeValueMap.keySet());
        return readMultipleValues(client, nodeIds)
                .thenCompose(currentValues -> {
                    try {
                        List<WriteValue> writeValues = new ArrayList<>();

                        // 对每个节点值进行类型转换
                        for (Map.Entry<String, Object> entry : nodeValueMap.entrySet()) {
                            String nodeId = entry.getKey();
                            Object originalValue = entry.getValue();
                            DataValue currentDataValue = currentValues.get(nodeId);

                            // 获取当前值的类型并转换新值
                            Object currentValue = currentDataValue != null && currentDataValue.getValue() != null
                                    ? currentDataValue.getValue().getValue()
                                    : null;
                            Object convertedValue = convertValueToMatchType(currentValue, originalValue);

                            logger.debug("节点 [{}] 类型转换 - 原始值: {}({}), 转换后: {}({})",
                                    nodeId,
                                    originalValue, originalValue != null ? originalValue.getClass().getSimpleName() : "null",
                                    convertedValue, convertedValue != null ? convertedValue.getClass().getSimpleName() : "null");

                            // 创建写入对象
                            NodeId targetNodeId = NodeId.parse(nodeId);
                            Variant variant = new Variant(convertedValue);
                            DataValue dataValue = new DataValue(variant, null, null);

                            WriteValue writeValue = new WriteValue(
                                    targetNodeId,
                                    AttributeId.Value.uid(),
                                    null,
                                    dataValue
                            );
                            writeValues.add(writeValue);
                        }

                        // 执行批量写入
                        return client.write(writeValues)
                                .thenApply(writeResponse -> {
                                    StatusCode[] statusCodes = writeResponse.getResults();
                                    if (statusCodes == null || statusCodes.length != nodeValueMap.size()) {
                                        logger.error("批量写入结果数量不匹配，预期: {}, 实际: {}", nodeValueMap.size(), statusCodes == null ? 0 : statusCodes.length);
                                        throw new OpcUaException("Write failed: result count mismatch, expected " + nodeValueMap.size() + ", got " + (statusCodes == null ? 0 : statusCodes.length));
                                    }

                                    Map<String, StatusCode> result = new HashMap<>();
                                    int successCount = 0;
                                    int i = 0;

                                    for (String nodeId : nodeValueMap.keySet()) {
                                        StatusCode statusCode = statusCodes[i++];
                                        result.put(nodeId, statusCode);

                                        if (statusCode.isGood()) {
                                            successCount++;
                                        } else {
                                            logger.warn("节点 [{}] 写入返回非成功状态码: {}", nodeId, statusCode);
                                        }
                                    }

                                    logger.debug("批量写入完成，总节点数: {}, 成功写入: {}", nodeValueMap.size(), successCount);
                                    return result;
                                });
                    } catch (Exception e) {
                        logger.error("批量写入类型转换失败", e);
                        throw new OpcUaException("Batch write type conversion failed", e);
                    }
                })
                .exceptionally(ex -> {
                    logger.error("批量写入发生异常", ex);
                    throw new OpcUaException("Batch write failed", ex);
                });
    }

    /**
     * 简化读取方法，直接返回值的字符串表示
     */
    public static String readValueAsString(String endpointUrl, String nodeId) throws OpcUaException {
        logger.info("读取单个节点值(字符串) - 节点: {}, 服务器: {}", nodeId, endpointUrl);
        DataValue dataValue = readValue(endpointUrl, nodeId);

        if (dataValue == null) {
            logger.warn("节点 [{}] 未返回任何数据", nodeId);
            return null;
        }

        Variant variant = dataValue.getValue();
        if (variant == null) {
            logger.warn("节点 [{}] 返回空的Variant对象", nodeId);
            return null;
        }

        Object value = variant.getValue();
        if (value == null) {
            logger.warn("节点 [{}] 返回空值", nodeId);
            return null;
        }

        return value.toString();
    }

    /**
     * 简化批量读取方法，直接返回值的字符串表示
     */
    public static Map<String, String> readValuesAsString(String endpointUrl, List<String> nodeIds) throws OpcUaException {
        logger.info("批量读取节点值(字符串) - 节点数量: {}, 服务器: {}", nodeIds.size(), endpointUrl);
        Map<String, DataValue> dataValues = readValues(endpointUrl, nodeIds);
        Map<String, String> result = new HashMap<>();

        for (Map.Entry<String, DataValue> entry : dataValues.entrySet()) {
            String nodeId = entry.getKey();
            DataValue value = entry.getValue();

            if (value != null && value.getValue() != null && value.getValue().getValue() != null) {
                result.put(nodeId, value.getValue().getValue().toString());
            } else {
                result.put(nodeId, null);
                logger.warn("节点 [{}] 的值为null或格式不正确", nodeId);
            }
        }

        return result;
    }

    /**
     * 辅助方法：将值转换为与目标类型匹配
     */
    private static Object convertValueToMatchType(Object targetValue, Object sourceValue) {
        if (targetValue == null || sourceValue == null) {
            logger.debug("目标值或源值为null，不进行类型转换");
            return sourceValue;
        }

        Class<?> targetClass = targetValue.getClass();
        Class<?> sourceClass = sourceValue.getClass();

        // 类型已匹配，无需转换
        if (targetClass.equals(sourceClass)) {
            logger.debug("类型已匹配 [{}]，不进行转换", targetClass.getSimpleName());
            return sourceValue;
        }

        // 数值类型转换
        try {
            if (targetClass == Integer.class && sourceValue instanceof Number) {
                return ((Number) sourceValue).intValue();
            } else if (targetClass == Float.class && sourceValue instanceof Number) {
                return ((Number) sourceValue).floatValue();
            } else if (targetClass == Double.class && sourceValue instanceof Number) {
                return ((Number) sourceValue).doubleValue();
            } else if (targetClass == Long.class && sourceValue instanceof Number) {
                return ((Number) sourceValue).longValue();
            } else if (targetClass == Short.class && sourceValue instanceof Number) {
                return ((Number) sourceValue).shortValue();
            } else if (targetClass == String.class) {
                return sourceValue.toString();
            } else if (sourceClass == String.class) {
                // 特殊处理：字符串转数值类型
                String strValue = (String) sourceValue;
                if (targetClass == Integer.class) {
                    return Integer.parseInt(strValue);
                } else if (targetClass == Float.class) {
                    return Float.parseFloat(strValue);
                } else if (targetClass == Double.class) {
                    return Double.parseDouble(strValue);
                } else if (targetClass == Long.class) {
                    return Long.parseLong(strValue);
                } else if (targetClass == Short.class) {
                    return Short.parseShort(strValue);
                }
            }
        } catch (Exception e) {
            logger.warn("类型转换失败: {} -> {}", sourceClass.getSimpleName(), targetClass.getSimpleName(), e);
        }

        logger.warn("无法转换类型 [{} -> {}]，将使用原始值", sourceClass.getSimpleName(), targetClass.getSimpleName());
        return sourceValue;
    }

    /**
     * 将列表拆分为批次
     */
    private static <T> List<List<T>> splitIntoBatches(List<T> list, int batchSize) {
        List<List<T>> batches = new ArrayList<>();
        for (int i = 0; i < list.size(); i += batchSize) {
            int end = Math.min(i + batchSize, list.size());
            batches.add(list.subList(i, end));
        }
        return batches;
    }

    /**
     * 将Map拆分为批次
     */
    private static <K, V> List<Map<K, V>> splitMapIntoBatches(Map<K, V> map, int batchSize) {
        List<Map<K, V>> batches = new ArrayList<>();
        Map<K, V> currentBatch = new HashMap<>();
        int count = 0;

        for (Map.Entry<K, V> entry : map.entrySet()) {
            currentBatch.put(entry.getKey(), entry.getValue());
            count++;

            if (count >= batchSize) {
                batches.add(currentBatch);
                currentBatch = new HashMap<>();
                count = 0;
            }
        }

        if (!currentBatch.isEmpty()) {
            batches.add(currentBatch);
        }

        return batches;
    }

    /**
     * 连接池条目类
     */
    private static class ClientPoolEntry {
        private final OpcUaClient client;
        private long lastUsedTime;

        public ClientPoolEntry(OpcUaClient client) {
            this.client = client;
            this.lastUsedTime = System.currentTimeMillis();
            logger.debug("创建新的连接池条目，客户端: {}", client);
        }

        /**
         * 检查连接是否过期
         */
        public boolean isExpired() {
            boolean expired = System.currentTimeMillis() - lastUsedTime > IDLE_TIMEOUT * 1000;
            if (expired) {
                logger.debug("连接池条目已过期，空闲时间: {}秒，超时阈值: {}秒", (System.currentTimeMillis() - lastUsedTime) / 1000, IDLE_TIMEOUT);
            }
            return expired;
        }

        /**
         * 更新最后使用时间
         */
        public void updateLastUsedTime() {
            long previousTime = lastUsedTime;
            this.lastUsedTime = System.currentTimeMillis();

            // 定义日期时间格式：年-月-日 时:分:秒
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()); // 使用系统默认时区

            // 格式化时间戳为字符串
            String previousTimeStr = formatter.format(Instant.ofEpochMilli(previousTime));
            String currentTimeStr = formatter.format(Instant.ofEpochMilli(lastUsedTime));

            logger.debug("更新连接池条目最后使用时间，之前: {}，现在: {}", previousTimeStr, currentTimeStr);
        }
    }

    /**
     * OPC UA 异常类 - 非受检异常
     */
    public static class OpcUaException extends RuntimeException {
        public OpcUaException(String message) {
            super(message);
        }

        public OpcUaException(String message, Throwable cause) {
            super(message, cause);
        }
    }
}
